package org.meetunexpectedly.service.user.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.meetunexpectedly.constant.RedisConstant;
import org.meetunexpectedly.service.user.FeedService;
import org.meetunexpectedly.util.DateUtil;
import org.meetunexpectedly.util.RedisCacheUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.*;

/**
 * @Description: 关注流服务实现类
 * @Author: pupil
 * @Date: 2024/08/20 下午 7:49
 */
@Service
public class FeedServiceImpl implements FeedService {

    // 获取的视频数量
    private final long videoCount = 50;

    @Autowired
    private RedisCacheUtil redisCacheUtil;

    @Autowired
    private RedisTemplate redisTemplate;

    private Random random = new Random();

    @Override
    @Async
    public void putOutBoxFeed(Long userId, Long videoId, Long time) {
        redisCacheUtil.zAdd(RedisConstant.USER_OUT_FOLLOW + userId, time, videoId, -1);
    }

    @Override
    @Async
    public void putInBoxFeed(Long userId, Long videoId, Long time) {
        redisCacheUtil.zAdd(RedisConstant.USER_IN_FOLLOW + userId, time, videoId, RedisConstant.HISTORY_TIME);
    }

    @Override
    @Async
    public void putInBoxFeed(Long videoId, Collection<Long> fansIds, Long time) {
        // 优化后
        for (Long fansId : fansIds) {
            // 查询该粉丝是否在线，在线才推送
            Object o = redisCacheUtil.get(RedisConstant.USER_ONLINE + fansId);
            if (!ObjectUtils.isEmpty(o)) {
                redisCacheUtil.zAdd(RedisConstant.USER_IN_FOLLOW + fansId, time, videoId, RedisConstant.HISTORY_TIME + random.nextInt(RedisConstant.EXPIRE_VARIANCE));
            }
        }
    }

    @Override
    @Async
    public void deleteOutBoxFeed(Long userId, Collection<Long> fans, Long videoId) {
        String fansKey = RedisConstant.USER_IN_FOLLOW;
        redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            for (Long fanId : fans) {
                // zRem 用于从有序集合中移除一个或多个成员，如果成员存在的话
                // 删除粉丝收件箱中该视频
                connection.zRem((fansKey + fanId).getBytes(), String.valueOf(videoId).getBytes());
            }
            // 删除用户的发件箱中该视频
            connection.zRem((RedisConstant.USER_OUT_FOLLOW + userId).getBytes(), String.valueOf(videoId).getBytes());
            return null;
        });
    }

    @Override
    @Async
    public void deleteInBoxFeed(Long userId, List<Long> videoIds) {
        redisCacheUtil.removeZSet(RedisConstant.USER_IN_FOLLOW + userId, videoIds.toArray());
    }

    @Override
    @Async
    public void initFollowFeed(Long userId, Collection<Long> followIds) {
        String fansKey = RedisConstant.USER_IN_FOLLOW;
        // 当前时间
        Date curDate = new Date();
        // 最后时间(当前时间的7天前)
        Date limitDate = DateUtil.addDateDays(curDate, -7);

        // 获取用户收件箱中最近视频的发布时间
        Set<ZSetOperations.TypedTuple<Long>> set = redisTemplate.opsForZSet().rangeWithScores(fansKey + userId, -1, -1);
        if (!ObjectUtils.isEmpty(set)) {
            // 最近视频的发布时间
            Double oldTime = set.iterator().next().getScore();
            init(userId, oldTime.longValue(), curDate.getTime(), followIds);
        } else {
            init(userId, limitDate.getTime(), curDate.getTime(), followIds);
        }
    }

    /**
     * 初始化用户收件箱
     *
     * @param userId    用户id
     * @param min       最早时间
     * @param max       最新时间
     * @param followIds 关注列表
     */
    private void init(Long userId, Long min, Long max, Collection<Long> followIds) {
        String followKey = RedisConstant.USER_OUT_FOLLOW;
        String userKey = RedisConstant.USER_IN_FOLLOW;
        // 查看关注人的发件箱
        List<Set<DefaultTypedTuple>> result = redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            for (Long followId : followIds) {
                // offset 偏移量为0从第一个匹配视频获取
                // 获取50个视频
                // todo 这里可能要进行优化，每个关注人都获取50个视频有点多
                connection.zRevRangeByScoreWithScores((followKey + followId).getBytes(), min, max, 0, videoCount);
            }
            return null;
        });

        ObjectMapper objectMapper = new ObjectMapper();
        // 放入用户收件箱
        redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            for (Set<DefaultTypedTuple> tuples : result) {
                if (!ObjectUtils.isEmpty(tuples)) {
                    for (DefaultTypedTuple tuple : tuples) {
                        Object videoId = tuple.getValue();
                        byte[] key = (userKey + userId).getBytes();
                        try {
                            connection.zAdd(key, tuple.getScore(), objectMapper.writeValueAsBytes(videoId));
                        } catch (JsonProcessingException e) {
                            e.printStackTrace();
                        }
                        // 设置过期时间
                        connection.expire(key, RedisConstant.HISTORY_TIME + random.nextInt(RedisConstant.EXPIRE_VARIANCE));
                    }
                }
            }
            return null;
        });
    }
}
